package com.dyt.itool.parser;

import com.dyt.itool.generator.*;
import com.dyt.itool.model.IProperty;
import com.dyt.itool.model.Struct;
import com.dyt.itool.model.impl.BasicProperty;
import com.dyt.itool.model.impl.ListProperty;
import com.dyt.itool.model.impl.MapProperty;
import com.dyt.itool.util.ByteBuffer;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MoilExcelStructParser extends AbstractParser {


    private static final byte TYPE_BOOLEAN = 'Z';
    private static final byte TYPE_BYTE = 'B';
    private static final byte TYPE_SHORT = 'S';
    private static final byte TYPE_INTEGER = 'I';
    private static final byte TYPE_FLOAT = 'F';
    private static final byte TYPE_LONG = 'J';
    private static final byte TYPE_STRING = 'L';

    private Logger logger = Logger.getLogger(MoilExcelStructParser.class);

    private Map<String, List<Struct>> structMap = new HashMap<>();
    private List<String> structNameList = new ArrayList<>();

    public MoilExcelStructParser() {

    }

    public void parseDir(String dirName) {
        File dir = new File(dirName);
        if (!dir.exists() || !dir.isDirectory()) {
            logger.debug("要解析的文件夹不存在");
            return;
        }

        File[] fileList = dir.listFiles();
        for (File file : fileList) {
            parseFile(file);
        }

    }

    public void parse(String filename) {

        File file = new File(filename);

        parseFile(file);
    }

    public void parseFile(File file) {
        logger.info("parse file " + file.getName());
        if (!file.exists() || !file.isFile()) {
            logger.debug("要解析的文件不存在");
            return;
        }

        if (!file.getName().endsWith(".xls")
                && !file.getName().endsWith(".xlsx")) {
            logger.debug("无法解析的文件名:" + file.getName());
            return;
        }

        Workbook wb = null;

        try {
            if (file.getName().endsWith("xls")) {
                wb = new HSSFWorkbook(new FileInputStream(file));
            } else if (file.getName().endsWith("xlsx")) {
                wb = new XSSFWorkbook(new FileInputStream(file));
            }
        } catch (Exception e) {
            e.printStackTrace();

            logger.debug(e.getMessage());
            return;
        }

        System.out.println("开始解析文件:" + file.getName());

        String structName = file.getName().replace(".xlsx", "").replace(".xls", "");

        List<Struct> structListItem = new ArrayList<>();
        structNameList.add(structName);
        structMap.put(structName, structListItem);

        parseDataStruct(wb, structListItem);

        System.out.println("解析文件成功:" + file.getName());
    }

    public void generateCode(Map<String, String> templateOutputMap) {
        for (String templateFile : templateOutputMap.keySet()) {

            String outputFile = templateOutputMap.get(templateFile);

            try {

                Map<String, Object> dataMap = new HashMap<String, Object>();
                dataMap.put("modelMap", structMap);
                dataMap.put("modelNameList", structNameList);
                dataMap.put("cutil", new CHelper());
                dataMap.put("javautil", new JavaHelper());
                dataMap.put("luautil", new LuaHelper());
                dataMap.put("typescriptutil", new TypeScriptHelper());

                VelocityUtils.generateFromVelocity(templateFile, outputFile,
                        dataMap);

                Writer standOut = new OutputStreamWriter(System.out);
                VelocityUtils.generateFromVelocity(templateFile, standOut,
                        dataMap);
                standOut.flush();

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void generateBin(String outputDir) {

        File dir = new File(outputDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        ByteBuffer buffer = new ByteBuffer();

        buffer.writeInt(structMap.size());//表文件数量

        for (String structName : structNameList) {
            List<Struct> structGroup = structMap.get(structName);

            buffer.writeUTF8(structName);//表文件名字

            buffer.writeInt(structGroup.size());//组数量

            for (Struct struct : structGroup) {
                try {
                    buffer.writeUTF8(struct.getStructName());//组名称

                    buffer.writeInt(struct.getPropertyList().size());//组字段数

                    for (IProperty property : struct.getPropertyList()) {
                        switch (property.getTypeName().toLowerCase()) {
                            case IProperty.BOOL:
                                buffer.writeByte(TYPE_BOOLEAN);
                                buffer.writeUTF8(property.getVarName());
                                buffer.writeBool(Boolean.valueOf(property.getValue().toString()));
                                break;
                            case IProperty.BYTE:
                                buffer.writeByte(TYPE_BYTE);
                                buffer.writeUTF8(property.getVarName());
                                buffer.writeByte(Byte.valueOf(property.getValue().toString()));
                                break;
                            case IProperty.SHORT:
                                buffer.writeByte(TYPE_SHORT);
                                buffer.writeUTF8(property.getVarName());
                                buffer.writeShort(Short.valueOf(property.getValue().toString()));
                                break;
                            case IProperty.INT:
                                buffer.writeByte(TYPE_INTEGER);
                                buffer.writeUTF8(property.getVarName());
                                buffer.writeInt(Integer.valueOf(property.getValue().toString()));
                                break;
                            case IProperty.FLOAT:
                                buffer.writeByte(TYPE_FLOAT);
                                buffer.writeUTF8(property.getVarName());
                                buffer.writeFloat(Float.valueOf(property.getValue().toString()));
                                break;
                            case IProperty.LONG:
                                buffer.writeByte(TYPE_LONG);
                                buffer.writeUTF8(property.getVarName());
                                buffer.writeLong(Long.valueOf(property.getValue().toString()));
                                break;
                            case IProperty.STRING:
                                buffer.writeByte(TYPE_STRING);
                                buffer.writeUTF8(property.getVarName());
                                buffer.writeUTF8(property.getValue().toString());
                                break;
                        }
                    }
                } catch (Exception e) {
                    logger.error(String.format("处理 %s 时出现异常:", struct.getStructName()), e);
                    System.exit(0);
                }
            }
        }

        try {

            FileOutputStream fos = new FileOutputStream(new File(dir,
                    "MoilDict.mbin"));
            byte[] bytes = buffer.array();

            fos.write(bytes, 0, bytes.length);
            fos.flush();
            fos.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    private boolean canAcceptColumn(String columnName) {
        // #都不读, $ 客户端不读， %服务器不读
        return columnName != null && (!columnName.startsWith("#")) && (!columnName.startsWith("$"));
    }


    private boolean isNoneRow(Sheet sheet, int rowIndex) {
        Row row = sheet.getRow(rowIndex);
        int columnSize = sheet.getRow(0).getPhysicalNumberOfCells();
        String groupName = getStringCellValue(row.getCell(0));
        boolean none = false;
        if (groupName.trim().isEmpty()) {
            none = true;
            for (int column = 1; column < columnSize; column++) {
                String value = getStringCellValue(row.getCell(column));
                if (value.trim().length() != 0 && !value.startsWith("#")) {
                    none = false;
                    break;
                }
            }
        }
        return none;
    }

    private void parseDataStruct(Workbook wb, List<Struct> dataStructList) {
        Sheet sheet = wb.getSheetAt(0);
        int rowSize = sheet.getPhysicalNumberOfRows();
        int columnSize = sheet.getRow(0).getPhysicalNumberOfCells();

        Struct dataStruct;

        for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {

            if (isNoneRow(sheet, rowIndex)) {
                continue;
            }


            if (isNoneRow(sheet, rowIndex + 1)) {
                throw new RuntimeException("字段名称不正确");
            }

            Row rowType = sheet.getRow(rowIndex);
            Row rowName = sheet.getRow(rowIndex + 1);
            Row rowDesc = sheet.getRow(rowIndex + 2);
            Row rowValue = sheet.getRow(rowIndex + 3);

            rowIndex += 3;

            String groupName = getStringCellValue(rowType.getCell(0));

            dataStruct = new Struct(groupName);
            dataStructList.add(dataStruct);

            for (int column = 1; column < columnSize; column++) {
                String type = getStringCellValue(rowType.getCell(column));
                if (type.trim().length() == 0) {
                    break;
                }
                String name = getStringCellValue(rowName.getCell(column));

                if (canAcceptColumn(name)) {
                    IProperty property = parseProperty(type, name);
                    String columnValue = getStringCellValue(rowValue.getCell(column));
                    property.setValue(columnValue);
                    dataStruct.addProperty(property);
                }
            }
        }
    }

    private IProperty parseProperty(String typeName, String varName) {

        typeName = typeName.trim();
        varName = varName.trim();
        if (varName.startsWith("%")) {
            varName = varName.substring(1);
        }

        // list中只能放基本数据类型
        if (typeName.toLowerCase().startsWith("list")) {
            String elementType = typeName.replace("list<", "").replace(">", "")
                    .trim();
            return new ListProperty("list", varName, new BasicProperty(
                    elementType, elementType));
        }

        // map中只能放基本数据类型
        if (typeName.toLowerCase().startsWith("map")) {
            String[] keyValue = typeName.replace("map<", "").replace(">", "")
                    .trim().split(",");
            String key = keyValue[0].trim();
            String value = keyValue[1].trim();

            key = this.basicTypeMap.get(key);
            value = this.basicTypeMap.get(value);

            return new MapProperty("map", varName, new BasicProperty(key, key),
                    new BasicProperty(value, value));
        }

        if (basicTypeMap.containsKey(typeName)) {
            return new BasicProperty(this.basicTypeMap.get(typeName), varName);
        }

        return null;
    }

    private String getStringCellValue(Cell cell) {

        if (cell == null) {
            return "";
        }

        String strCell = "";
        switch (cell.getCellType()) {
            case HSSFCell.CELL_TYPE_STRING:
                strCell = cell.getStringCellValue();
                break;
            case HSSFCell.CELL_TYPE_NUMERIC:
                strCell = String.valueOf(cell.getNumericCellValue());
                break;
            case HSSFCell.CELL_TYPE_BOOLEAN:
                strCell = String.valueOf(cell.getBooleanCellValue());
                break;
            case HSSFCell.CELL_TYPE_BLANK:
                strCell = "";
                break;
            case HSSFCell.CELL_TYPE_FORMULA:
                try {
                    strCell = String.valueOf(cell.getNumericCellValue());
                } catch (IllegalStateException e) {
                    strCell = String.valueOf(cell.getRichStringCellValue());
                }
                break;
            default:
                strCell = "";
                break;
        }
        if (strCell.equals("") || strCell == null) {
            return "";
        }

        return strCell.trim();
    }

}
